<script src="decoder.js"></script>

<canvas id="preview" width="400" height="100" style="border: 1px solid #d0d0d0;"></canvas><br>
<canvas id="digital" width="400" height="50" style="border: 1px solid #d0d0d0;"></canvas>
<pre id="log"></pre>

<script>
class Preview
{
  constructor(id)
  {
    this.elem = document.querySelector(id);
    this.width = this.elem.width;
    this.height = this.elem.height;
    this.ctx = this.elem.getContext("2d");
  }

  clear()
  {
    this.elem.width = this.elem.width;
  }

  drawPoly(p, c, w)
  {
    this.ctx.strokeStyle = c;
    this.ctx.lineJoin="round";
    this.ctx.lineWidth = w ? w : 1;
    this.ctx.beginPath();

    this.ctx.moveTo(p[0].x, p[0].y);
    for (var i=0; i<p.length; i++)
      this.ctx.lineTo(p[i].x, p[i].y);
    this.ctx.stroke();
  }
}

var preview = new Preview("#preview");
var digital = new Preview("#digital");
var decoder = new Decoder();

window.document.addEventListener('OscDataChanged', (e) => { 
  var data1 = e.detail.wave[0];
  var data2 = e.detail.wave[1];
  var trigger = e.detail.trigThreshold;
  var ypos = y => preview.height-y*preview.height/256;
  var points1 = [], points2 = [], pointsd = [];
  var l = data1.length;
  var d = new Array(l);

//  var kTimebase = 1000e-6  /30*1000*1000; // microseconds per sample
//TODO: sync timebase
  var kTimebase = 5e-3  /30*1000*1000; // microseconds per sample
  var cur = 0;
  var last = 0;
  var times = [];
  var cnt = 0;

  for (var i=0; i<l; i++)
  {
    points1.push({x:i/l*preview.width, y:ypos(data1[i])});
    points2.push({x:i/l*preview.width, y:ypos(data2[i])});
    cur = (data1[i] > trigger) ? 1 : 0;

    if (i==0) 
      last == cur;

    cnt++;
    if (last != cur)
    {
      times.push(cnt);
      cnt = 0;
      last = cur;
    }

    d[i] = cur;
    pointsd.push({x:i/l*preview.width, y:25-d[i]*20+10});
  }

  times.shift(); // initial zero interval
//  console.log(times);
  times = times.map(x => Math.floor(x * kTimebase))
//  console.log(times);

  preview.clear();
  preview.drawPoly([{x:0, y:ypos(trigger)}, {x:preview.width, y:ypos(trigger)}], "#d0d0d0");
  preview.drawPoly(points1, "#b00000");
  preview.drawPoly(points2, "#0000b0");

  digital.clear();
  digital.drawPoly(pointsd, "#000000");

  if (0 && times.length > 100)
  {
    var a = times[0+4];
    var b = times[1+4];
    if (a < b*1.3 && a > b*0.7)
    {
      var comp = Math.floor((a-b)/2);
      console.log("comp = " + comp);
      for (var i = 0; i<times.length; i++)
      {
        if ((i&1)==1)
          times[i] += comp;
        else
          times[i] -= comp;
      }
    }
  }
  if (times.length > 100)
    console.log(times.join());

  process(times);
  var r = decoder.decode(times); 
  console.log(JSON.stringify(r));

  for (var rr in r)
  {
    document.querySelector("#log").innerHTML += JSON.stringify(r[rr]) + "\n";
  }
//  console.log(e.detail.timebase);
  e.detail.sender.dispatchEvent(new CustomEvent("StartSingle"));
}, false);

function process(seq)
{
  var aligned = seq.map(x=>Math.floor(x/500+0.5));
  if (aligned[0] == 1 && aligned[1] == 18 && aligned[1+9*4*2+2] == 18 && aligned[1+(9*4*2+2)*2] == 18 && aligned[1+(9*4*2+2)*3] == 18)
  {
    var bits = [];
    for (var j=0; j<9*4; j++)
    {
      var spacer = aligned[2+j*2];
      var data = aligned[2+j*2+1];
      if (spacer != 1)
        throw "wrong spacer value";
      if (data != 4 && data != 8)
        throw "wrong data value";
      bits.push(data == 4 ? 0 : 1);
    }
    var nibs = [];
    for (var j=0; j<9; j++)
    {
      var b3 = bits.shift();
      var b2 = bits.shift();
      var b1 = bits.shift();
      var b0 = bits.shift();
      var nib = b0 + b1 * 2 + b2 * 4 + b3 * 8;
      nibs.push(nib);
    }
    console.log(nibs.map(v=>v.toString(16)).join(""));
    nibs = [nibs[1] + nibs[0]*16, nibs[3] + nibs[2]*16, nibs[5] + nibs[4]*16, nibs[7] + nibs[6]*16, nibs[8]*16];
    console.log(decode(nibs));
  }
}

function decode(b)
{
    var reverse8=(x) =>
    {
      x = (x & 0xF0) >> 4 | (x & 0x0F) << 4;
      x = (x & 0xCC) >> 2 | (x & 0x33) << 2;
      x = (x & 0xAA) >> 1 | (x & 0x55) << 1;
      return x;
    }

    // reverse bit order
    var rb = [ reverse8(b[0]), reverse8(b[1]), reverse8(b[2]),
            reverse8(b[3]), reverse8(b[4]) ];

    var sum_nibbles =
        (rb[0] >> 4) + (rb[0] & 0xF)
      + (rb[1] >> 4) + (rb[1] & 0xF)
      + (rb[2] >> 4) + (rb[2] & 0xF)
      + (rb[3] >> 4) + (rb[3] & 0xF);

    var checksum = rb[4] & 0x0F;  // just make sure the 10th nibble does not contain junk
    if (checksum != (sum_nibbles & 0xF))
        return {error:"checksum"}; // wrong checksum

    /* IIIICCII B???TTTT TTTTTSSS HHHHHHH1 XXXX */
    var negative_sign = (b[2] & 7);
    var temp          = ((rb[2]&0x1F) << 4) | (rb[1]>> 4);
    var humidity      = (rb[3] & 0x7F) - 28;
    var sensor_id     = (rb[0] & 0x0F) | ((rb[0] & 0xC0)>>2);
    var battery_low   = b[1] >> 7;
    var channel       = (b[0]>>2) & 3;

    var tempC = (negative_sign ? -( (1<<9) - temp ) : temp ) * 0.1;
    return {temp:tempC, humidity:humidity, id:sensor_id, battery_low:battery_low, channel:channel};
}

var sample = [
[466,500,500,466,1000,966,966,966,500,466,533,466,500,500,966,966,966,966,533,466,466,500,500,466,1000,466,500,1000,966,500,466,466,533,466,500,966,500,466,500,466,533,433,500,500,1000,933,533,466,500,466,533,433,1000,966,500,466,500,500,500,433,533,466,500,466,500,500,500,466,500,500,966,966,1000,966,500,466,500,466,1000,966,500,466,500,466,500,466,500,500,500,500,966,966,500,466,500,466,533,466,966,966,1000,966,966,500,500,466,500,466,533,933,500,500,266],
[466,466,500,500,466,533,466,466,500,500,500,466,500,466,466,533,466,500,500,466,466,533,466,500,500,466,500,466,500,500,500,466,466,533,466,500,500,466,466,500,500,500,500,433,500,500,500,1000,933,1000,966,966,966,1033,433,500,1000,1000,433,533,933,533,466,966,500,500,466,500,466,500,466,533,933,1000,466,533,466,466,500,500,466,500,966,1000,966,966,466,500,1000,966,966,1000,500,466,500,466,500,500,466,500,466,533,466,500,466,500,466,500,500,500,466,500,466,500,466,533,466,466,500,500,500,466,500,466,500,500,466,533,466,466,500,500,466,533,933,1000,966,966,466,533,966,966,466,500,533,466,466,500,966,1000,466,500,1000,966,933,1000,500,500,466,500,466,500,500,500,433,500,1000,500,466,466,500,1000,966,500,466,1000,500,500,433,500,966,1000,466,500,966,1033,933,1000,466],
[466,533,433,500,466,533,466,533,433,500,500,500,466,533,433,500,466,533,466,533,433,500,466,533,466,500,466,500,466,533,466,500,466,500,466,533,466,500,433,533,466,500,500,500,433,533,466,1000,933,1000,1000,966,933,1033,466,500,966,1000,466,500,966,500,466,966,500,500,466,533,466,466,500,500,966,966,466,533,466,500,466,500,466,533,966,966,966,1033,433,500,966,1033,900,1000,500,500,433,533,466,500,500,500,433,533,466,533,466,500,433,533,466,533,433,533,433,533,466,500,500,500,433,533,433,533,466,533,433,533,433,533,466,533,433,500,500,500,966,966,966,1033,400,533,966,1000,433,533,466,533,466,500,933,1033,466,466,966,1000,966,1000,466,533,433,500,500,500,466,500,500,466,966,533,466,500,466,1000,933,533,433,1000,500,500,466,500,933,1033,466,500,966,1000,933,1000,466],
[533,566,366,566,400,600,366,600,400,566,400,566,433,533,433,566,400,566,400,600,400,533,466,533,433,533,433,533,466,500,466,500,466,500,500,500,500,466,500,500,466,500,466,500,500,500,466,966,1000,966,966,500,500,466,466,533,466,500,500,933,533,466,500,466,1000,966,1000,933,533,466,466,500,500,466,966,1000,966,1000,466,466,533,466,500,466,1000,466,500,966,1000,466,500,500,466,500,500,966,500,466,500,466,500,466,500,500,966,1000,500,466,500,500,466,466,1000,966,500,466,500,500,500,466,500,466,500,500,466,500,500,466,500,500,966,966,1000,933,500,500,500,466,1000,966,466,500,500,466,533,433,500,500,500,466,1000,966,500,466,500,466,500,500,966,966,1000,966,966,500,500,466,500,466,533,933,500,500,266]
];

var sample = [[500,9000,333,2166,333,4166,333,4166,333,2000,500,2000,500,4000,500,2000,333,4166,333,2000,500,2000,500,4000,500,2000,333,4166,500,4000,500,2000,500,4000,333,2000,500,4166,333,2000,500,4000,500,2000,500,1833,500,2000,500,2000,500,4000,500,4000,500,4000,500,4000,500,4000,500,4000,500,4000,500,4166,333,4166,500,4000,500,4000,500,2000,500,9000,500,1833,500,4000,500,4166,333,2000,500,2000,500,4000,500,2000,333,4166,500,1833,500,2000,500,4000,500,2000,333,4166,500,4000,500,2000,333,4166,500,1833,500,4000,500,2000,500,4000,500,2000,500,2000,333,2000,500,2000,500,4000,500,4000,500,4000,500,4000,500,4000,500,4000,500,4000,500,4166,500,4000,500,4000,500,4000,500,2000,500,9000,500,1833,500,4000,500,4000,500,2000,500,2000,500,4000,500,2000,333,4166,333,2000,500,2000,500,4000,500,2000,500,4000,500,4000,500,2000,333,4166,500,1833,500,4000,500,2000,500,4000,500,2000,500,2000,333,2000,500,2000,500,4000,500,4000,500,4000,500,4000,500,4000,500,4000,500,4000,500,4166,500,4000,333,4166,500,4000,500,2000,500,9000,500,1833,500,4166,333,4166,333,2000,500,2000,500,4000,500,2000,333,4166,500,1833,500,2000,500,4000,500,2000,333,4166,500,4000,500,2000,333,4166,333,2000,500,4166,333,2000,500,4000,500,2000,500,2000,333,2000,500,2000,500,4000,500,4000,500,4000,500,4000,500,4000,500,4000,500,4166,333,4166,333,4166,500,4000,500,4000,500,2000,500]];

setTimeout( () => {
for (var i in sample)
{
  var seq = sample[i];
/*
.map(x=>Math.floor(x/500+0.5))
"[1,18,1,4,1,8,1,8,1,4,1,4,1,8,1,4,1,8,1,4,1,4,1,8,1,4,1,8,1,8,1,4,1,8,1,4,1,8,1,4,1,8,1,4,1,4,1,4,1,4,1,8,1,8,1,8,1,8,1,8,1,8,1,8,1,8,1,8,1,8,1,8,1,4,1,
    18,1,4,1,8,1,8,1,4,1,4,1,8,1,4,1,8,1,4,1,4,1,8,1,4,1,8,1,8,1,4,1,8,1,4,1,8,1,4,1,8,1,4,1,4,1,4,1,4,1,8,1,8,1,8,1,8,1,8,1,8,1,8,1,8,1,8,1,8,1,8,1,4,1,
    18,1,4,1,8,1,8,1,4,1,4,1,8,1,4,1,8,1,4,1,4,1,8,1,4,1,8,1,8,1,4,1,8,1,4,1,8,1,4,1,8,1,4,1,4,1,4,1,4,1,8,1,8,1,8,1,8,1,8,1,8,1,8,1,8,1,8,1,8,1,8,1,4,1,
    18,1,4,1,8,1,8,1,4,1,4,1,8,1,4,1,8,1,4,1,4,1,8,1,4,1,8,1,8,1,4,1,8,1,4,1,8,1,4,1,8,1,4,1,4,1,4,1,4,1,8,1,8,1,8,1,8,1,8,1,8,1,8,1,8,1,8,1,8,1,8,1,4,1]"
*/
  process(seq);
  console.log(decoder.decode(sample[i]));
}
}, 150);

</script>